/***************************************************************************
 *   Copyright (C) 2008 by Ivan Blagoev Topolsky   *
 *   ivan.topolsky@genebio.com   *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU Library General Public License as       *
 *   published by the Free Software Foundation; either version 3 of the    *
 *   License, or (at your option) any later version.                       *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU Library General Public     *
 *   License along with this program; if not, write to the                 *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/

#include <iostream>
#include <sstream>

#define KEEP_LISTS
#include "filterparse.h"

namespace xeno {

namespace merlin {

using namespace std;
	
using namespace boost;
using namespace boost::regex_constants;

// Abusing C preprocessors macros :
// - first we define the list of fields in FILTERPARSE_ALL_REGEXP
// - "_" are simple "name" + RegExp pattern
// - "_o" aren't optionnal but are OBLIGATORY "name" + RegExp pattern
// - "_m" is for the final mass list (has blanks at the other side)
// - "__" have two capturing component in the pattern
// - "___" have three capturing component in the pattern

// Then each time we build a list (RegExp, Names, Enums, etc...)
// the _,_o,__,___ macros are redeclared depending on purpose

#define FILTERPARSE_ALL_REGEXP	\
f_(Instrument,	"ITMS|TQMS|SQMS|TOFMS|FTMS|Sector")	\
f_(SectorsOnly,"[^+\\w]+")	\
f_o(Polarity,	"\\+|-")	\
f_(ScanData,	"c|p")	\
f_(IonizationMode,	"EI|CI|FAB|ESI|APCI|NSI|TSP|FD|MALDI|GD")	\
f_(Corona,	"\\!?corona")	\
f_(PhotoIoniziation,	"\\!?pi")	\
f__(SourceCID,	"(\\!?sid)(?:=(\\S+))?")	\
f_(DetectorSet,	"\\!det|det")	\
f_(Turboscan,	"\\!?t")	\
f_(Enhanced,	"\\!?E")	\
f_(Dependent,	"\\!?d")	\
f_(SkipSA,	"SA")	\
f_(Wideband,	"\\!?w")	\
f_(AccuateMass,	"\\!AM|AM|AMI|AME")	\
f_(Ultra,	"\\!?u")	\
f_o(ScanType,	"Full|SIM|SRM|CRM|Q1MS|Q3MS|BSCAN|ESCAN|z")	\
f__(MSLevel,	"(ms(\\d*))")	\
f___(Parent, Activation,	"(?:([.\\d]+)@(cid|mpd|ecd|pqd|etd|hcd|sa|ptr)(-?[.\\d]+)\\s+)*")	\
f_(FreeRegion, ".*")	\
f_m(MassRanges, "\\s*\\[(\\s*([.\\d]+)\\s*-\\s*([.\\d]+)\\s*,?)+\\]")


/*
 * Builds the RegExp
 */
// here we concatenate the various patterns, wraping them as needed in optionnal fields, etc.
#define f_(NAME,PAT) "(?:(" PAT ")\\s+)?"
#define f_o(NAME,PAT) "(" PAT ")\\s+"
#define f_m(NAME,PAT) PAT
#define f__(NAME,PAT) "(?:" PAT "\\s+)?"
#define f___(NAME1,NAME,PAT)  PAT 
static const regex re( FILTERPARSE_ALL_REGEXP , perl | icase);
#undef f___
#undef f__
#undef f_m
#undef f_o
#undef f_



#define f_o(NAME,PAT) f_(NAME,PAT)
#define f_m(NAME,PAT) f_(NAME,PAT) f_(NAME##Lo,PAT) f_(NAME##Hi,PAT)
#define f__(NAME,PAT) f_(NAME,PAT) f_(NAME##Extra,PAT)
#define f___(NAME1,NAME,PAT) f_(NAME1,PAT) f__(NAME,PAT)

/*
 * Builds a list of names
 */
// Here We stringify names. In Fields featuring more than 1 field, we stringify a suffisient ammount of fields
#define f_(NAME,PAT) new string ( #NAME ),
static const string* names[] = {
	new const string ("All"),
	FILTERPARSE_ALL_REGEXP
	NULL
};	
#undef f_

/*
 * Builds a list of handy Enums for addressing the matches
 */
// Same as names, execpt, we simply make variable names instead of strings
#define f_(NAME,PAT) e##NAME,
typedef enum {
	eFullString,
	FILTERPARSE_ALL_REGEXP
	eLast
} MATCHES;
#undef f_

#undef f___
#undef f__
#undef f_m
#undef f_o


FilterParse::FilterParse(const string& newtxt ) : txt(newtxt), rmatch() {
	regex_search(txt, rmatch, re, match_extra);

	// load the MAP
	#define f_(NAME) 	 (* this)[*(names[e##NAME])] = rmatch[e##NAME];
	STRING_METHODS
	f_(SourceCID)
	f_(MSLevel)
	#undef f_
}

// automatically build the string-returning methods
#define f_(NAME) string FilterParse::NAME() const { return rmatch[e##NAME]; }
	STRING_METHODS;
#undef f_

int	FilterParse::MSLevel()	const { int v=1;	stringstream s ( rmatch[eMSLevelExtra] );	s >> v; return v; }
double	FilterParse::SourceCID()const { double v=0.;	stringstream s ( rmatch[eSourceCIDExtra] );	s >> v; return v; }
double	FilterParse::Energy()	const { double v=0.;	stringstream s ( rmatch[eActivationExtra] );	s >> v; return v; }
double	FilterParse::Parent()	const { double v=0.;	stringstream s ( rmatch[eParent] );		s >> v; return v; }

double FilterParse::operator() (const string& key) {
	if (key == "SourceCID") {
		return lexical_cast<double> ( rmatch[eSourceCIDExtra] );
	} else 	if (key == "MSLevel") {
		return lexical_cast<double> ( rmatch[eMSLevelExtra] );
	} else if (key == "Energy") {
		return lexical_cast<double> ( rmatch[eActivationExtra] );
	} else 	if (key == "Parent") {
		return lexical_cast<double> ( rmatch[eParent] );
	}
	return 0.;
}

// TODO MassRanges


ostream& operator<< (ostream& o, FilterParse &p) {
	o << "[" << p.txt  << "]" << endl;
	
	if(! p.rmatch.empty()) {
		unsigned i, j;
		cout << "** Match found **\n   Sub-Expressions:\n";
		for(i = 0; i < p.rmatch.size(); ++i)
			o << "      $" << i << " = " << (names[i] ? *names[i] : "(oops)") << " = \t\"" << p.rmatch[i] << "\"\n";
/*
		o << "   Captures:\n";
		for(i = 0; i < p.rmatch.size(); ++i) {
			o << "      $" << i << " = {";
			for(j = 0; j < p.rmatch.captures(i).size(); ++j) {
				o << ( j ? ", " : " ")
					<< "\"" << p.rmatch.captures(i)[j] << "\"";
			}
			o << " }\n";
		}
*/
	} else {
		o << "** No Match found **\n";
	}
	return o;
}


}
}
